/*
 * Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "los_net_container.h"
#include "los_config.h"

extern struct ContainerConfig g_containerConfig;
UINT32 g_currentNetContainerNum = 1;

static struct netif *NetifAlloc() {
    UINT32 size = sizeof(struct netif);

    struct netif *netif = LOS_MemAlloc(m_aucSysMem1, size);

    if (netif == NULL) {
        PRINT_ERR("failed to malloc netif memory.\n");
        return NULL;
    }
    (VOID)memset_s(netif, size, 0, size);

    return netif;
}

static void InitLoopNetif(NetContainer *netContainer) {
    struct netif *loop_netif = NetifAlloc();

    if (loop_netif == NULL) {
        PRINT_ERR("init loop netif failed, netif is null.\n");
        return;
    }

    netContainer->group->loop_netif = loop_netif;
    netif_init(netContainer->group);
}

static struct netif * CreateVethNetif(NetContainer *netContainer) {
    struct netif *veth = NetifAlloc();

    if (veth == NULL) {
        PRINT_ERR("create veth netif failed, netif is null.\n");
        return NULL;
    }

    veth_init(veth, netContainer->group);

    return veth;
}

static void InitVethPair(NetContainer *netContainer) {
    struct netif *veth1 = CreateVethNetif(netContainer);

    if (veth1 == NULL) {
        PRINT_ERR("failed to create veth1.\n");
        return;
    }

    struct netif *veth2 = CreateVethNetif(&g_rootNetContainer);

    if (veth2 == NULL) {
        PRINT_ERR("failed to create veth2.\n");
        return;
    }

    veth1->peer = veth2;
    veth2->peer = veth1;
}

static void InitContainerNetifs(NetContainer *netContainer) {
    InitLoopNetif(netContainer);
    InitVethPair(netContainer);
}

static void InitNetGroup(NetContainer *netContainer) {
    UINT32 size = sizeof(struct net_group);
    struct net_group *group = LOS_MemAlloc(m_aucSysMem1, size);
    if (group == NULL) {
        PRINT_ERR("failed to malloc net group memory.\n");
        return;
    }
    (VOID)memset_s(group, size, 0, size);
    netContainer->group = group;
}

void FreeNetif(struct netif *netif) {
    if (netif) {
        if (netif->peer != NULL) {
            netif_remove(netif->peer);
            LOS_MemFree(m_aucSysMem1, netif->peer);
        }
        netif_remove(netif);
        LOS_MemFree(m_aucSysMem1, netif);
    }
}

INT32 ReplaceNetContainerInBundle(ContainerBundle* bundle, struct NetContainer *targetNetContainer)
{
    if (bundle->netContainer) {
        DerefNetContainer(bundle->netContainer);
    }

    bundle->netContainer = targetNetContainer;
    GetNetContainer(targetNetContainer);

    return LOS_OK;
}

static NetContainer *CreateNewNetContainer(NetContainer *oldNetContainer) {
    if (g_currentNetContainerNum > g_containerConfig.MaxNetContainer) {
        PRINT_ERR("CreateNetContainer failed, reach to max container config.\n");
        return NULL;
    }

    NetContainer *newNetContainer = NULL;

    UINT32 size = sizeof(NetContainer);

    if (!(newNetContainer = LOS_MemAlloc(m_aucSysMem1, size))) {
        PRINT_ERR("failed to malloc NetContainer memory.\n");
        return NULL;
    }

    UINT32 intSave;
    SCHEDULER_LOCK(intSave);
    g_currentNetContainerNum += 1;
    SCHEDULER_UNLOCK(intSave);

    (VOID)memset_s(newNetContainer, size, 0, size);

    ContainerAllocVnum(&(newNetContainer->containerBase.vnum));

    newNetContainer->count = 1;
    InitNetGroup(newNetContainer);
    InitContainerNetifs(newNetContainer);

    return newNetContainer;
}

NetContainer *HandleNetContainer(unsigned long flags, NetContainer *runUtsContainer)
{
    if (!(flags & CLONE_NEWNET)) {
        GetNetContainer(runUtsContainer);
        return runUtsContainer;
    }

    return CreateNewNetContainer(runUtsContainer);
}

void FreeNetContainer(NetContainer *netContainer) {
    struct netif *freeNetif = netContainer->group->netif_list;
    struct netif *nextNetif;

    while (freeNetif != NULL)
    {
        nextNetif = freeNetif->next;
        FreeNetif(freeNetif);
        freeNetif = nextNetif;
    }

    LOS_MemFree(m_aucSysMem1, netContainer->group);
    LOS_MemFree(m_aucSysMem1, netContainer);

    UINT32 intSave;
    SCHEDULER_LOCK(intSave);
    g_currentNetContainerNum -= 1;
    SCHEDULER_UNLOCK(intSave);
}

NetContainer g_rootNetContainer = {0};

static struct net_group *do_get_curr_process_net_group(void) {
    LosProcessCB *processCB = OsCurrProcessGet();
    NetContainer *netContainer;
    if(!processCB || !processCB->containerBundle || !processCB->containerBundle->netContainer) {
        netContainer = &g_rootNetContainer;
    } else {
        netContainer = processCB->containerBundle->netContainer;
    }
    return netContainer->group;
}

static void do_set_netif_net_group(struct netif *netif, struct net_group *group) {
    netif->group = group;
}

static struct net_group *do_get_net_group_from_netif(struct netif *netif) {
    return netif != NULL ? netif->group : NULL;
}

static void do_set_ippcb_net_group(struct ip_pcb *pcb, struct net_group *group) {
    pcb->group = group;
}

static struct net_group *do_get_net_group_from_ippcb(struct ip_pcb *pcb) {
    return pcb != NULL ? pcb->group : NULL;
}

struct net_group_ops container_net_group_ops = {
    .get_curr_process_net_group = do_get_curr_process_net_group,
    .set_netif_net_group = do_set_netif_net_group,
    .get_net_group_from_netif = do_get_net_group_from_netif,
    .set_ippcb_net_group = do_set_ippcb_net_group,
    .get_net_group_from_ippcb = do_get_net_group_from_ippcb,
};

void InitNetContainer(NetContainer *netContainer)
{
    netContainer->count = 1;
    netContainer->group = get_root_net_group();
    set_default_net_group_ops(&container_net_group_ops);
    ContainerAllocVnum(&(netContainer->containerBase.vnum));
}

NetContainer *GetCurrNetContainer(void)
{
    return &g_rootNetContainer;
}
